/**
 * 
 */
package org.hive.testserver.domain.model.schedule;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;

import org.apache.commons.collections.ComparatorUtils;
import org.apache.commons.collections.Transformer;
import org.apache.commons.collections.TransformerUtils;
import org.apache.commons.lang.SerializationUtils;
import org.hive.testserver.domain.model.Slave;
import org.hive.testserver.domain.model.Test;
import org.hive.testserver.domain.model.WorkSheet;

public abstract class BaseAllocatorLink implements AllocatorLink, Serializable {
	private transient List<Test> tests;
	protected AllocatorLink next;
	
	private static final long serialVersionUID = 1L;
	
	public BaseAllocatorLink(AllocatorLink next) {
		super();
		this.next = next;
	}

	public boolean accept(Test test, Slave slave, WorkSheet workSheet) {
		if (shouldAccept(test, slave, workSheet)) {
			tests().add(test);
			return true;
		} else {
			return next.accept(test, slave, workSheet);
		}
	}
	
	protected List<Test> tests() {
		if (tests == null) {
			tests = new ArrayList<Test>();
		}
		return tests;
	}
	
	protected abstract boolean shouldAccept(Test test, Slave slave, WorkSheet workSheet);

	public ScheduledTest scheduleNextTest(Slave slave, WorkSheet workSheet) {
		if (! tests().isEmpty()) {
			Collections.sort(tests(), priorityComparator());
			Test test = tests().get(0);
			tests.remove(0);
			return new ScheduledTest(test, scheduledReason());
		} else {
			return next.scheduleNextTest(slave, workSheet);
		}
	}

	protected abstract ScheduleReason scheduledReason();

	@SuppressWarnings("unchecked")
	protected Comparator<? super Test> priorityComparator() {
		Comparator sortByName = ComparatorUtils.transformedComparator(String.CASE_INSENSITIVE_ORDER, TransformerUtils.invokerTransformer("getName"));
		Comparator sortByDuration = ComparatorUtils.transformedComparator(ComparatorUtils.NATURAL_COMPARATOR, new Transformer(){
		
			public Object transform(Object input) {
				Test test = (Test) input;
				return test.testHistory().averageDuration();
			}
		});
		return ComparatorUtils.chainedComparator(sortByDuration, sortByName);
	}
	
	public boolean shouldConsiderTestFinished(Test test) {
		return next.shouldConsiderTestFinished(test);
	}

	public AllocatorLink createWorkingCopy() {
		return (AllocatorLink) SerializationUtils.clone(this);
	}
}